package TrackModel;

import TrainModel.TrainModel;
import application.MapElement;

/**
 * @author Jordan
 *The Block class is used to represent a block of track. It holds all of the information about a block,
 *such as the block ID, grade, elevation, length, etc. Most of the variables are set to protected,
 *so the class has lots of getters and setters for the various attributes.
 */
public class Block implements TrainModelToTrackModel
{
	protected MapElement mapElement;
	protected int blockID;
	protected double grade, elevation, staticFriction, kineticFriction, length, speedLimit, railTemperature;
	protected int[] connections, xPositions, yPositions;
	public boolean isUnderground, heaterState, hasTrain, hasBrokenRail, hasPowerFailure, hasCircuitFailure, hasTransponder;
	protected TrainModel theTrain;
	protected int section;
	
	/**Constructor for a Block object that sets all of the various attributes of a Block
	 * @param theBlock - The String array containing the various attributes that make up a Block object
	 */
	public Block(String[] theBlock)
	{
		blockID = Integer.parseInt(theBlock[0]);
		section = Integer.parseInt(theBlock[1]);
		length = Double.parseDouble(theBlock[2]);
		grade = Double.parseDouble(theBlock[3]);
		speedLimit = Double.parseDouble(theBlock[4]);
		elevation = Double.parseDouble(theBlock[5]);
		String[] xString = theBlock[6].split(":");
		String[] yString = theBlock[7].split(":");
		xPositions = new int[2];
		yPositions = new int[2];
		for (int i = 0; i < 2; i++)
		{
			xPositions[i] = Integer.parseInt(xString[i]);
			yPositions[i] = Integer.parseInt(yString[i]);
		}
		isUnderground = theBlock[8].equals("1");
		hasTransponder = theBlock[9].equals("1");
		String[] theConnections = theBlock[10].split(":");
		connections = new int[theConnections.length];
		for (int i = 0; i < theConnections.length; i++)
		{
			connections[i] = Integer.parseInt(theConnections[i]);
		}
		hasTrain = false;
		hasCircuitFailure = false;
		hasPowerFailure = false;
		hasBrokenRail = false;
		heaterState = false;
		staticFriction = Utilities.TrainConstants.dWheelToTrackFrictionCoeffStatic;
		kineticFriction = Utilities.TrainConstants.dWheelToTrackFrictionCoeffKinetic;
	}
	
	/**
	 * @return Returns the section number that the Block belongs to
	 */
	public int getSection()
	{
		return section;
	}
	
	/**
	 * @return Returns false if there is a Power Failure, Circuit Failure, or Broken Rail for the Block.
	 * 			Otherwise returns true.
	 */
	public boolean isHealthy()
	{
		if (hasPowerFailure || hasCircuitFailure || hasBrokenRail)
			return false;
		else
			return true;
	}
	
	/**
	 * @param state - The new state of a broken rail
	 * @return Returns the new state that the broken rail was just set to
	 */
	public boolean setBrokenRail(boolean state)
	{
		hasBrokenRail = state;
		return hasBrokenRail;
	}
	
	/**
	 * @param state - The new state of a circuit failure
	 * @return Returns the new state that the circuit failure was just set to
	 */
	public boolean setCircuitFailure(boolean state)
	{
		hasCircuitFailure = state;
		return hasCircuitFailure;
	}
	
	/**
	 * @param state - The new state of a power failure
	 * @return Returns the new state that the power failure was just set to
	 */
	public boolean setPowerFailure(boolean state)
	{
		hasPowerFailure = state;
		return hasPowerFailure;
	}
	
	/**
	 * @param state - The new state of the track heater
	 * @return Returns the new state that the heater was just set to
	 */
	public boolean setHeaterState(boolean state)
	{
		heaterState = state;
		return heaterState;
	}
	
	/* (non-Javadoc)
	 * @see TrackModel.TrainModelToTrackModel#getFrictionCoeffStatic()
	 * Returns the current static friction coefficient for the Block
	 */
	public double getFrictionCoeffStatic()
	{
		return staticFriction;
	}
	
	/* (non-Javadoc)
	 * @see TrackModel.TrainModelToTrackModel#getFrictionCoeffKinetic()
	 * Returns the current kinetic friction coefficient for the block
	 */
	public double getFrictionCoeffKinetic()
	{
		return kineticFriction;
	}
	
	/**
	 * @param friction - The new coefficient of static friction that you want to set
	 */
	public void setFrictionCoeffStatic(double friction)
	{
		staticFriction = friction;
	}
	
	/**
	 * @param friction - The new coefficient of kinetic friction that you want to set
	 */
	public void setFrictionCoeffKinetic(double friction)
	{
		kineticFriction = friction;
	}
	
	/**
	 * @param temperature - The new temperature that you want to set for the rail of the Block
	 */
	public void setRailTemperature(double temperature)
	{
		railTemperature = temperature;
	}
	
	/**
	 * @return - Returns the block ID of the block
	 */
	public int getBlockID()
	{
		return blockID;
	}
	
	/* (non-Javadoc)
	 * @see TrackModel.TrainModelToTrackModel#getGrade()
	 * Returns the grade of the block
	 */
	public double getGrade()
	{
		return grade;
	}
	
	/**
	 * @return Returns the elevation of the block in meters
	 */
	public double getElevation()
	{
		return elevation;
	}
	
	/**
	 * @return Returns the length of the block in meters
	 */
	public double getLength()
	{
		return length;
	}
	
	/**
	 * @return Returns the speed limit of the block in km/h
	 */
	public double getSpeedLimit()
	{
		return speedLimit;
	}
	
	/**
	 * @return Returns the rail temperature in degrees Fahrenheit 
	 */
	public double getRailTemperature()
	{
		return railTemperature;
	}
	
	
	/**
	 * @return Returns an integer array of the block's coordinates in the form {x1, y1, x2, y2}
	 */
	public int[] getLocation()
	{
		int[] location = {xPositions[0], yPositions[0], xPositions[1], yPositions[1]};
		return location;
	}
	
	/**
	 * @return Returns an integer array of the block IDs of the blocks that can be directly traveled to from this block
	 */
	public int[] getConnections()
	{
		return connections;
	}

	/**
	 * @return Returns true if the Block is underground, otherwise returns false
	 */
	public boolean isUnderground()
	{
		return isUnderground;
	}
	
	/**
	 * @return Returns true if the heater is turned on, otherwise returns false
	 */
	public boolean getHeaterState()
	{
		return heaterState;
	}
	
	/**Adds a Train Model object to the Block
	 * @param theTrain - The Train Model object to be added to the Block
	 */
	public void addTrain(TrainModel theTrain)
	{
		this.theTrain = theTrain;
		hasTrain = true;
	}
	
	/**
	 * Removes a Train Model object by setting it to null for the Block
	 */
	public void removeTrain()
	{
		theTrain = null;
		hasTrain = false;
	}
	
	/**
	 * @return Returns the Train Model object that is on this Block
	 */
	public TrainModel getTrain()
	{
		return theTrain;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 * Returns a String representation of all of the important variables for this Block
	 */
	public String toString()
	{
		String other;
		if (isUnderground)
			other = new String("Underground\n");
		else
			other = new String("None\n");
		if (connections.length == 1)
			return "Block ID: "+blockID+"\tLength: "+length+"\tGrade: "+grade+"\tSpeed Limit: "+speedLimit+"\nElevation: "+elevation+"\t"
					+ "Connected to Block: "+connections[0]+"\nOther information: "+other;
		else
			return "Block ID: "+blockID+"\tLength: "+length+"\tGrade: "+grade+"\tSpeed Limit: "+speedLimit+"\nElevation: "+elevation+"\t"
					+ "Connected to Blocks: "+connections[0]+", "+connections[1]+"\nOther information: "+other;
	}
	
}